package org.mymybatis.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @program: mySSM
 * @description: 数据会话类,实际执行sql的对象
 * 目前仅实现inset方法和select方法
 * @author:wjp
 * @create: 2025-02-15 23:04
 **/
public class SqlSession {
    /**
     * 执行sql需要连接对象以及mappedStatements
     * 而这两个属性在SqlSessionFactory中都用，因此直接属性注入SqlSessionFactory
     */
    private SqlSessionFactory sqlSessionFactory;

    public SqlSession(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    public int insert(String sqlId, Object obj) {

        int count = 0;

        // 开启连接
        sqlSessionFactory.getTransaction().openSession();
        Connection connection = sqlSessionFactory.getTransaction().getConnection();

        // 根据sqlId拿到要执行的sql文本
        MappedStatement mappedStatement = sqlSessionFactory.getMappedStatementMap().get(sqlId);
        /**
         * 此时的sql是不符合真正执行的格式的如 insert into user values(#{username},#{age})
         * 而真正执行时需要的sql格式为insert into user values(?,?)
         * 因此需要将sql进行替换
         * 并且还需要在使用sql预编译对象对具体的？设置具体的值
         * 难点1 占位符数量未知
         * 难点2 不同的数据类型需调用不同的set方法，如setInt，setString
         * 难点3 需要根据#{}中具体的属性名将参数对象obj中具体的字段的值放入
         **/
        String sqlText = mappedStatement.getSqlText();

        PreparedStatement preparedStatement = replaceSqlTextAndSetValue(connection,sqlText,obj);
        try {
            count = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        return count;
    }

    public Object selectOne(String sqlId, Object param) {
        sqlSessionFactory.getTransaction().openSession();
        Connection connection = sqlSessionFactory.getTransaction().getConnection();
        MappedStatement mappedStatement = sqlSessionFactory.getMappedStatementMap().get("com.wjp.test.UserMapper.selectOne");
        PreparedStatement preparedStatement = replaceSqlTextAndSetValue(connection, mappedStatement.getSqlText(), param);
        Object o = null;
        try {
            ResultSet resultSet = preparedStatement.executeQuery();
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = Class.forName(resultType);
            o = resultTypeClass.getDeclaredConstructor().newInstance();
            // 根据返回值类型封装结果集对象
            while (resultSet.next()) {
                // 逐行读取结果
                // 反射放入对应的值
                Field[] declaredFields = resultTypeClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    String fieldTypeSimpleName = declaredField.getType().getSimpleName();
                    String fieldName = declaredField.getName();
                    // 获取set方法
                    Method setMethod = resultTypeClass.getDeclaredMethod("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), declaredField.getType());
                    if ("String".equalsIgnoreCase(fieldTypeSimpleName)){
                        setMethod.invoke(o, resultSet.getString(fieldName));
                    }else if ("Integer".equalsIgnoreCase(fieldTypeSimpleName)){
                        setMethod.invoke(o, resultSet.getInt(fieldName));
                    }
                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return o;
    }

    private PreparedStatement replaceSqlTextAndSetValue(Connection connection, String sqlText, Object obj) {

        PreparedStatement preparedStatement = null;
        //首先将sql文本进行替换，构建预编译对象
        String executableSql = sqlText.replaceAll("#\\{[A-Za-z0-9_$]*}", "?");
        try {
            preparedStatement = connection.prepareStatement(executableSql);

            // 对sql文本进行遍历查找其中的#{},并对其赋值
            // 记录开始查找的下标
            int formIndex = 0;
            // 记录当前参数的下标（jdbc中以1开始）
            int paramIndex = 1;
            while (true){
                // insert into user values(#{username},#{age})
                int beginIndex = sqlText.indexOf("#",formIndex);
                if (beginIndex == -1){
                    break;
                }
                int endIndex = sqlText.indexOf("}",formIndex);
                formIndex = endIndex + 1;

                // 获取属性名
                String filedName = sqlText.substring(beginIndex + 2, endIndex);
                Class<?> filedClass = obj.getClass();
                // 根据属性名获取字段对象
                Field declaredField = filedClass.getDeclaredField(filedName);
                // 获取字段类型的简单名字
                String fieldTypeSimpleName = declaredField.getType().getSimpleName();
                // 获取字段对应的get方法用于获取对应的属性值
                String filedGetMethodName = "get" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
                Method getMethod = filedClass.getDeclaredMethod(filedGetMethodName);
                Object filedValue = getMethod.invoke(obj);

                // 利用当前参数下表和参数类型以及参数值进行赋值
                if ("String".equals(fieldTypeSimpleName)){
                    preparedStatement.setString(paramIndex,filedValue.toString());
                }else if ("Integer".equals(fieldTypeSimpleName)){
                    preparedStatement.setInt(paramIndex,Integer.parseInt(filedValue.toString()));
                }
                paramIndex++;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return preparedStatement;
    }

}
